﻿using System;
using System.Xml;
using System.Data;
using System.Text;
using System.Configuration;
using System.Security.Principal;
using SdmxMl.Registry;

namespace SdmxMl.Manager
{
    #region Registry connection status

    public enum RepositoryState
    {
        Ok,
        Unused,
        OnError
    }
    #endregion

    /// <summary> Proxy Class to access TinyRegistry </summary>
    public class TinyRegistryProxy
    {
        #region Static properties

        /// <summary>DataTable holding all urn availables (from config info)</summary>
        private static DataTable dtUrn;
        public static DataTable DtURN
        {
            get { return dtUrn; }
        }

        /// <summary> Window user identity</summary>
        private static string userIdentity;
        public  string User
        {
            get { return userIdentity; }
        }


        private static RepositoryState _state;
        /// <summary>
        /// Registry usable if Uri defined and no error occured.
        /// </summary>
        public static RepositoryState State
        {
            get { return _state; }
        }

        public static string _url;
        /// <summary>
        /// If registry is used the configuration must provide the _url to use.
        /// </summary>
        public static string URL
        {
            get
            {
                if (_url == null)
                    _url = PrimaryUrl;
                UpdateState();
                return _url;
            }
            set
            {
                _url = value;
                UpdateState();
            }
        }

        /// <summary> Main TinyRegistry urn (from config) and alternates registration </summary>
        public static string PrimaryUrl
        {
            get
            {
                string url = string.Empty;

                if (ConfigurationSettings.AppSettings["TinyRegistryUrl"] != null)
                    url = ConfigurationSettings.AppSettings["TinyRegistryUrl"].ToString().Trim();

                dtUrn = new DataTable("Urn");
                dtUrn.Columns.Add(new DataColumn("Usage", typeof(string)));
                dtUrn.Columns.Add(new DataColumn("Urn", typeof(string)));

                string[] arr = url.Split(';');
                if (arr.Length == 2)
                {
                    url = arr[1];
                    dtUrn.Rows.Add(new object[] { arr[0], url });
                }
                else
                    dtUrn.Rows.Add(new object[] { "Main", url });

                try
                {
                    if (ConfigurationSettings.AppSettings["TinyRegistryAlternateUrl"] != null)
                    {
                        // Presented as Usage;http;usage;http
                        string u = ConfigurationSettings.AppSettings["TinyRegistryAlternateUrl"].ToString().Trim();
                        arr = u.Split(';');
                        int index = 0;
                        if (arr.Length > 0 && (arr.Length % 2) == 0)
                        {
                            while (index < arr.Length)
                            {
                                dtUrn.Rows.Add(new object[] { arr[index], arr[index+1] });
                                index += 2;
                            }
                        }
                    }
                }
                catch { }

                return url;
            }
        }

        /// <summary> Update state of communication with registry </summary>
        private static void UpdateState()
        {
            string s = string.Empty;
            if (_url.Length == 0)
                _state = RepositoryState.Unused;
            else
            {
                try
                {
                    Uri u = new Uri(_url);
                    _state = RepositoryState.Ok;
                    if (State == RepositoryState.Ok)
                    {
                        SdmxMl.TinyRegistry.Repository proxy = new SdmxMl.TinyRegistry.Repository();
                        proxy.Url = _url;
                        proxy.Test();
                    }
                }
                catch (Exception ex)
                {
                    s = ex.Message;
                    _state = RepositoryState.OnError;
                }
            }
        }
        

        /// <summary>
        /// If registry is used the configuration must provide the version as key to use.
        /// </summary>
        private static string _vs;
        public static string VS
        {
            
            get
            {
                if (_vs == null)
                {
                    if (ConfigurationSettings.AppSettings["TinyRegistryVers"] != null)
                    {
                        string v = ConfigurationSettings.AppSettings["TinyRegistryVers"].ToString().Trim();

                        _vs = v[0] == '4' ? v : "44" + v;
                    }
                    else
                        _vs = string.Empty;
                }


                return _vs;
            }
        }

        /// <summary> Retrieve naming associated to currently selected urn </summary>
        /// <returns>the associated target naming</returns>
        public static string GetTargetInfo()
        {
            string target = "?";
            if (DtURN != null)
            {
                DataTable dt = DtURN;
                foreach (DataRow row in dt.Rows)
                    if (row[1].ToString() == _url)
                    {
                        target = row[0].ToString();
                        break;
                    }
            }
            return target;
        }

        public static bool UserIsAdmin { get; set; }

        #endregion // Static properties


        #region Public Properties

        //DEC 10.08.2011 : Version of WS used
        public  double Version { get; set; }

        public bool BackupAutomatic { get; set; }

        #endregion


        #region Internal Members

        private SdmxMl.TinyRegistry.Repository smsProxy;

        #endregion // Internal Members

        #region C'tor

        // Constructor
        public TinyRegistryProxy()
        {
            try
            {
                userIdentity = WindowsIdentity.GetCurrent().Name;

                // Ensure state updated
                string url = URL;
                if (State == RepositoryState.Ok)
                {
                    StatusMessageType statusMsg = new StatusMessageType();

                    smsProxy = new SdmxMl.TinyRegistry.Repository();
                    smsProxy.Url = url;

                    //let's do an access and Get version of WS used
                    Version = 0;
                    statusMsg = new StatusMessageType(smsProxy.Test());
                    if (statusMsg.IsStatusMessage == true)
                    {
                        string versionLongue = statusMsg.Message.GetFirst();
                        string parseStringPass = versionLongue.Split(':')[1];
                        string[]parts = parseStringPass.Trim().Split('.');

                        Version = Convert.ToDouble(parts[0] + "." + parts[1]);
                    }

                    #region DEC 10.08.2011 : Backup version management
                    BackupAutomatic = false;
                    UserIsAdmin = false;
                    if (Version >= 1.0)
                    {
                        BackupAutomatic = smsProxy.BackupAutomaticEnable();
                        UserIsAdmin = IsAdministrator();
                    }
                    #endregion
                }
            }
            catch 
            {
                _state = RepositoryState.OnError;
            }
        }
        #endregion

        #region Public access to Registry service methods

        public XmlDocument GetArtefact(string arType, string fullIdent)
        {
            XmlDocument xDoc = null;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.GetArtefactOS(userIdentity, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                    if (statusMsg.IsStatusMessage == false)
                    {
                        xDoc = new XmlDocument();
                        xDoc.LoadXml(el.OuterXml);
                    }
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to fetch metadata from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
                if (statusMsg.IsStatusMessage)
                {
                    throw new ApplicationException("Unable to fetch metadata from registry." + Environment.NewLine + "Reason: " + statusMsg.Message);
                }
            }

            return xDoc;
        }

        public bool ExistArtefact(string arType, string fullIdent)
        {
            bool exist = false;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.ExistArtefact(arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to check metadata in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to check Artefact existence. Reason: " + statusMsg.Message);
                }
                else
                    exist = statusMsg.Status == StatusType.Success; // sucess if exist, warning if not existing
            }


            return exist;
        }

        public void DeleteArtefact(string arType, string fullIdent)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.DeleteArtefact(userIdentity, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to delete metadata in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to delete Artefact. Reason: " + statusMsg.Message);
                }
            }
        }

        public void SetDisseminationStatus(string artType, string fullId, string diffusionCd)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.SetDisseminationStatus(userIdentity, VS, artType, fullId, diffusionCd);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to set diffusion status to registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(artType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullId).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to set diffusion status to registry. Reason: " + statusMsg.Message);
                }
            }
        }

        #region DEC update 17.08.2011 : Implement method to bacup artefact before submission
        public void SubmitArtefactBackup(string arType, string fullIdent, XmlElement element)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.SubmitArtefactBackup(userIdentity, VS, arType, fullIdent, element);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to submit metadata to registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to submit artefact. Reason: " + statusMsg.Message);
                }
            }
        }
        #endregion

        public void SubmitArtefact(string arType, string fullIdent, XmlElement element)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.SubmitArtefact(userIdentity, VS, arType, fullIdent, element);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to submit metadata to registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to submit artefact. Reason: " + statusMsg.Message);
                }
            }
        }

        public void CheckOut(string arType, string fullIdent)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.CheckOut(userIdentity, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to check out artefact in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to lock artefact in registry ! : " + statusMsg.Message);
                }
            }
        }

        public void UndoCheckOut(string arType, string fullIdent)
        {
            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.UndoCheckOut(userIdentity, VS, arType, fullIdent);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to unlock artefact in registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullIdent).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to check out artefact in registry ! : " + statusMsg.Message);
                }

            }
        }

        /// <summary>Retrieves information list of artefact for specified type </summary>
        /// <param name="arType">Artefact type</param>
        /// <returns>A dataset holding the information table</returns>
        public System.Data.DataSet DirInfo(string arType)
        {
            System.Data.DataSet ds = new System.Data.DataSet();
            if (State == RepositoryState.Ok)
            {
                try
                {
                    ds = smsProxy.DirInfo(arType);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to obtain metadata list from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(arType).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
            }

            return ds;
        }

        #region DEC 10.08.2011 : Add information on backup version

        /// <summary>Determines if backup info exists</summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <returns>True if exists</returns>
        public bool IsBackupExist(string ar, string fullId)
        {
            bool bReturn = false;
            DataSet ds = GetBackupList(ar, fullId);

            if (ds != null && ds.Tables.Count == 1 && ds.Tables[0].Rows.Count > 0)
                bReturn = true;

            return (bReturn);
        }

        /// <summary> Get list of backup of specified artefact</summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <returns>A dataset holding backup list information</returns>
        public DataSet GetBackupList(string ar, string fullId)
        {
            DataSet dsReturn = new DataSet();

            if (State == RepositoryState.Ok)
            {
                try
                {
                    dsReturn = smsProxy.GetArtefactBackupList(ar, fullId);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to obtain metadata list from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(ar).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
            }

            return (dsReturn);
        }

        /// <summary> Retrieve a backuped Artefact document </summary>
        /// <param name="ar">Artefact type</param> <param name="fullId">Artefact Key</param>
        /// <param name="versionNumber">Backup number</param>
        /// <returns></returns>
        public XmlDocument GetArtefactBackup(string ar, string fullId, string versionNumber)
        {
            XmlDocument xDoc = null;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.GetArtefactBackup(userIdentity,
                        VS,
                        ar,
                        fullId,
                        versionNumber);

                    statusMsg = new StatusMessageType(el);
                    if (statusMsg.IsStatusMessage == false)
                    {
                        xDoc = new XmlDocument();
                        xDoc.LoadXml(el.OuterXml);
                    }
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to fetch metadata from registry.").Append(Environment.NewLine);
                    sb.Append("Type: ").Append(ar).Append(Environment.NewLine);
                    sb.Append("Full Id: ").Append(fullId).Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }
                if (statusMsg.IsStatusMessage)
                {
                    throw new ApplicationException("Unable to fetch metadata from registry." + Environment.NewLine + "Reason: " + statusMsg.Message);
                }
            }

            return xDoc;
        }
        #endregion

        /// <summary>
        /// Is window used a repository administrator
        /// </summary>
        /// <returns>True id administrator</returns>
        public bool IsAdministrator()
        {
            bool isAdmin = false;

            if (State == RepositoryState.Ok)
            {
                StatusMessageType statusMsg = new StatusMessageType();
                try
                {
                    XmlElement el = smsProxy.IsAdministrator(userIdentity, VS);
                    statusMsg = new StatusMessageType(el);
                }
                catch (Exception ex)
                {
                    StringBuilder sb = new StringBuilder(256);
                    sb.Append("Unable to get information about administrative user").Append(Environment.NewLine);
                    sb.Append("Reason: ").Append(ex.Message);

                    _state = RepositoryState.OnError;

                    throw new ApplicationException(sb.ToString(), ex);
                }

                if (statusMsg.Status == StatusType.Failure)
                {
                    throw new ApplicationException("Unable to get information about administrative user. Reason: " + statusMsg.Message);
                }
                else
                    isAdmin = statusMsg.Status == StatusType.Success && statusMsg.Message.GetFirst().ToLower() == "true";
            }


            return isAdmin;
        }



        #endregion
    }
}
